
function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint8Array(buf));
}

function str2ab(str) {
  var buf = new ArrayBuffer(str.length);
  var bufView = new Uint8Array(buf);
  for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

async function generateKey() {

    return await window.crypto.subtle.generateKey(
        {
            name: "AES-GCM",
            length: 256 
        },
        true, // extractable
        ["encrypt", "decrypt"]
    ).then(key => { return key; });

}

async function exportJwkKey(key){

    return await window.crypto.subtle.exportKey("jwk", key).then(key => { return key.k; });

}

async function importJwkKey(jwkKey){

    return await window.crypto.subtle.importKey(
        "jwk",
        {
            k: jwkKey,
            alg: "A256GCM",
            ext: true,
            key_ops: ["encrypt", "decrypt"],
            kty: "oct",
        },
        {
            name: "AES-GCM",
            length: 256
        },
        false, // extractable
        ["decrypt"]
    ).then(key => { return key; });

}

async function encryptMessage(message,key){

    const encryptedMessage = await window.crypto.subtle.encrypt(
        { name: "AES-GCM", iv: new Uint8Array(16)},
        key,
        new TextEncoder().encode(JSON.stringify(message))
    ).then(encryptedMessage => { return encryptedMessage; });
    
    return ab2str(encryptedMessage);
}

async function decryptMessage(encryptedMessage,key){
    
    const encrypted = str2ab(encryptedMessage);
    
    const decrypted = await window.crypto.subtle.decrypt(
        {
            name: "AES-GCM",
               iv: new Uint8Array(16) 
        },
        key,
        encrypted
    ).then(decryptedMessage => { return decryptedMessage; });

    return new window.TextDecoder().decode(new Uint8Array(decrypted));
}


const key = await generateKey();
const jwkKey = await exportJwkKey(key);
const encryptedMessage = await encryptMessage({SomeMessage:"This should work even if JSON"},key);

const keyFromJwk = await importJwkKey(jwkKey);
const decryptedMessage = await decryptMessage(encryptedMessage,keyFromJwk);

